/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <media/msm_isp.h>
#include "msm_sd.h"
#include "msm_cci.h"
#include "msm_cam_cci_hwreg.h"
#include "msm_camera_io_util.h"

#define V4L2_IDENT_CCI 50005
#define CCI_I2C_QUEUE_0_SIZE 64
#define CCI_I2C_QUEUE_1_SIZE 16
#define CYCLES_PER_MICRO_SEC_DEFAULT 4915
#define CCI_MAX_DELAY 1000000

#define CCI_TIMEOUT msecs_to_jiffies(100)

/* TODO move this somewhere else */
#define MSM_CCI_DRV_NAME "msm_cci"

#undef CDBG
#define CDBG(fmt, args...) pr_debug(fmt, ##args)

/* Max bytes that can be read per CCI read transaction */
#define CCI_READ_MAX 12
#define CCI_I2C_READ_MAX_RETRIES 3
#define CCI_I2C_MAX_READ 8192
#define CCI_I2C_MAX_WRITE 8192

static struct v4l2_subdev *g_cci_subdev;

static struct msm_cam_clk_info cci_clk_info[CCI_NUM_CLK_MAX];

static void msm_cci_set_clk_param(struct cci_device *cci_dev,
	struct msm_camera_cci_ctrl *c_ctrl)
{
	struct msm_cci_clk_params_t *clk_params = NULL;
	enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
	enum i2c_freq_mode_t i2c_freq_mode = c_ctrl->cci_info->i2c_freq_mode;

	if (cci_dev->master_clk_init[master])
		return;
	clk_params = &cci_dev->cci_clk_params[i2c_freq_mode];

	if (MASTER_0 == master) {
		msm_camera_io_w_mb(clk_params->hw_thigh << 16 |
			clk_params->hw_tlow,
			cci_dev->base + CCI_I2C_M0_SCL_CTL_ADDR);
		msm_camera_io_w_mb(clk_params->hw_tsu_sto << 16 |
			clk_params->hw_tsu_sta,
			cci_dev->base + CCI_I2C_M0_SDA_CTL_0_ADDR);
		msm_camera_io_w_mb(clk_params->hw_thd_dat << 16 |
			clk_params->hw_thd_sta,
			cci_dev->base + CCI_I2C_M0_SDA_CTL_1_ADDR);
		msm_camera_io_w_mb(clk_params->hw_tbuf,
			cci_dev->base + CCI_I2C_M0_SDA_CTL_2_ADDR);
		msm_camera_io_w_mb(clk_params->hw_scl_stretch_en << 8 |
			clk_params->hw_trdhld << 4 | clk_params->hw_tsp,
			cci_dev->base + CCI_I2C_M0_MISC_CTL_ADDR);
	} else if (MASTER_1 == master) {
		msm_camera_io_w_mb(clk_params->hw_thigh << 16 |
			clk_params->hw_tlow,
			cci_dev->base + CCI_I2C_M1_SCL_CTL_ADDR);
		msm_camera_io_w_mb(clk_params->hw_tsu_sto << 16 |
			clk_params->hw_tsu_sta,
			cci_dev->base + CCI_I2C_M1_SDA_CTL_0_ADDR);
		msm_camera_io_w_mb(clk_params->hw_thd_dat << 16 |
			clk_params->hw_thd_sta,
			cci_dev->base + CCI_I2C_M1_SDA_CTL_1_ADDR);
		msm_camera_io_w_mb(clk_params->hw_tbuf,
			cci_dev->base + CCI_I2C_M1_SDA_CTL_2_ADDR);
		msm_camera_io_w_mb(clk_params->hw_scl_stretch_en << 8 |
			clk_params->hw_trdhld << 4 | clk_params->hw_tsp,
			cci_dev->base + CCI_I2C_M1_MISC_CTL_ADDR);
	}
	cci_dev->master_clk_init[master] = 1;
	return;
}

static void msm_cci_flush_queue(struct cci_device *cci_dev,
	enum cci_i2c_master_t master)
{
	int32_t rc = 0;

	msm_camera_io_w_mb(1 << master, cci_dev->base + CCI_HALT_REQ_ADDR);
	rc = wait_for_completion_timeout(
		&cci_dev->cci_master_info[master].reset_complete, CCI_TIMEOUT);
	if (rc < 0) {
		pr_err("%s:%d wait failed\n", __func__, __LINE__);
	} else if (rc == 0) {
		pr_err("%s:%d wait timeout\n", __func__, __LINE__);

		/* Set reset pending flag to TRUE */
		cci_dev->cci_master_info[master].reset_pending = TRUE;

		/* Set proper mask to RESET CMD address based on MASTER */
		if (master == MASTER_0)
			msm_camera_io_w_mb(CCI_M0_RESET_RMSK,
				cci_dev->base + CCI_RESET_CMD_ADDR);
		else
			msm_camera_io_w_mb(CCI_M1_RESET_RMSK,
				cci_dev->base + CCI_RESET_CMD_ADDR);

		/* wait for reset done irq */
		rc = wait_for_completion_timeout(
			&cci_dev->cci_master_info[master].reset_complete,
			CCI_TIMEOUT);
		if (rc <= 0)
			pr_err("%s:%d wait failed %d\n", __func__, __LINE__,
				rc);
	}
	return;
}

static int32_t msm_cci_validate_queue(struct cci_device *cci_dev,
	uint32_t len,
	enum cci_i2c_master_t master,
	enum cci_i2c_queue_t queue)
{
	int32_t rc = 0;
	uint32_t read_val = 0;
	uint32_t reg_offset = master * 0x200 + queue * 0x100;
	read_val = msm_camera_io_r_mb(cci_dev->base +
		CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
	CDBG("%s line %d CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d len %d max %d\n",
		__func__, __LINE__, read_val, len,
		cci_dev->cci_i2c_queue_info[master][queue].max_queue_size);
	if ((read_val + len + 1) > cci_dev->
		cci_i2c_queue_info[master][queue].max_queue_size) {
		uint32_t reg_val = 0;
		uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8);
		CDBG("%s:%d CCI_I2C_REPORT_CMD\n", __func__, __LINE__);
		msm_camera_io_w_mb(report_val,
			cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
			reg_offset);
		read_val++;
		CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d\n",
			__func__, __LINE__, read_val);
		msm_camera_io_w_mb(read_val, cci_dev->base +
			CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
		reg_val = 1 << ((master * 2) + queue);
		CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__);
		msm_camera_io_w_mb(reg_val, cci_dev->base +
			CCI_QUEUE_START_ADDR);
		CDBG("%s line %d wait_for_completion_timeout\n",
			__func__, __LINE__);
		rc = wait_for_completion_timeout(&cci_dev->
			cci_master_info[master].reset_complete, CCI_TIMEOUT);
		if (rc <= 0) {
			pr_err("%s: wait_for_completion_timeout %d\n",
				 __func__, __LINE__);
			if (rc == 0)
				rc = -ETIMEDOUT;
			msm_cci_flush_queue(cci_dev, master);
			return rc;
		}
		rc = cci_dev->cci_master_info[master].status;
		if (rc < 0)
			pr_err("%s failed rc %d\n", __func__, rc);
	}
	return rc;
}

static int32_t msm_cci_data_queue(struct cci_device *cci_dev,
	struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue)
{
	uint16_t i = 0, j = 0, k = 0, h = 0, len = 0;
	int32_t rc = 0;
	uint32_t cmd = 0, delay = 0;
	uint8_t data[11];
	uint16_t reg_addr = 0;
	struct msm_camera_i2c_reg_setting *i2c_msg =
		&c_ctrl->cfg.cci_i2c_write_cfg;
	uint16_t cmd_size = i2c_msg->size;
	struct msm_camera_i2c_reg_array *i2c_cmd = i2c_msg->reg_setting;
	enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;

	if (i2c_cmd == NULL) {
		pr_err("%s:%d Failed line\n", __func__,
			__LINE__);
		return -EINVAL;
	}

	if ((!cmd_size) || (cmd_size > CCI_I2C_MAX_WRITE)) {
		pr_err("%s:%d Failed line\n", __func__, __LINE__);
		return -EINVAL;
	}

	CDBG("%s addr type %d data type %d\n", __func__,
		i2c_msg->addr_type, i2c_msg->data_type);

	if (i2c_msg->addr_type >= MSM_CAMERA_I2C_ADDR_TYPE_MAX) {
		pr_err("%s failed line %d\n", __func__, __LINE__);
		return -EINVAL;
	}
	if (i2c_msg->data_type >= MSM_CAMERA_I2C_DATA_TYPE_MAX) {
		pr_err("%s failed line %d\n", __func__, __LINE__);
		return -EINVAL;
	}
	/* assume total size within the max queue */
	while (cmd_size) {
		CDBG("%s cmd_size %d addr 0x%x data 0x%x", __func__,
			cmd_size, i2c_cmd->reg_addr, i2c_cmd->reg_data);
		delay = i2c_cmd->delay;
		data[i++] = CCI_I2C_WRITE_CMD;
		if (i2c_cmd->reg_addr)
			reg_addr = i2c_cmd->reg_addr;
		/* either byte or word addr */
		if (i2c_msg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
			data[i++] = reg_addr;
		else {
			data[i++] = (reg_addr & 0xFF00) >> 8;
			data[i++] = reg_addr & 0x00FF;
		}
		/* max of 10 data bytes */
		do {
			if (i2c_msg->data_type == MSM_CAMERA_I2C_BYTE_DATA) {
				data[i++] = i2c_cmd->reg_data;
				reg_addr++;
			} else {
				if ((i + 1) <= 10) {
					data[i++] = (i2c_cmd->reg_data &
						0xFF00) >> 8; /* MSB */
					data[i++] = i2c_cmd->reg_data &
						0x00FF; /* LSB */
					reg_addr += 2;
				} else
					break;
			}
			i2c_cmd++;
		} while (--cmd_size && !i2c_cmd->reg_addr && (i <= 10));
		data[0] |= ((i-1) << 4);
		len = ((i-1)/4) + 1;
		rc = msm_cci_validate_queue(cci_dev, len, master, queue);
		if (rc < 0) {
			pr_err("%s: failed %d", __func__, __LINE__);
			return rc;
		}
		for (h = 0, k = 0; h < len; h++) {
			cmd = 0;
			for (j = 0; (j < 4 && k < i); j++)
				cmd |= (data[k++] << (j * 8));
			CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x%x\n",
				__func__, cmd);
			msm_camera_io_w_mb(cmd, cci_dev->base +
				CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
				master * 0x200 + queue * 0x100);
		}
		if ((delay > 0) && (delay < CCI_MAX_DELAY)) {
			cmd = (uint32_t)((delay * cci_dev->cycles_per_us) /
				0x100);
			cmd <<= 4;
			cmd |= CCI_I2C_WAIT_CMD;
			CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x%x\n",
				__func__, cmd);
			msm_camera_io_w_mb(cmd, cci_dev->base +
				CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
				master * 0x200 + queue * 0x100);
		}
		i = 0;
	}
	return rc;
}

static int32_t msm_cci_write_i2c_queue(struct cci_device *cci_dev,
	uint32_t val,
	enum cci_i2c_master_t master,
	enum cci_i2c_queue_t queue)
{
	int32_t rc = 0;
	uint32_t reg_offset = master * 0x200 + queue * 0x100;
	CDBG("%s:%d called\n", __func__, __LINE__);
	rc = msm_cci_validate_queue(cci_dev, 1, master, queue);
	if (rc < 0) {
		pr_err("%s: failed %d", __func__, __LINE__);
		return rc;
	}
	CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val 0x%x:0x%x\n",
		__func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
		reg_offset, val);
	msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
		reg_offset);
	return rc;
}

static int32_t msm_cci_i2c_read(struct v4l2_subdev *sd,
	struct msm_camera_cci_ctrl *c_ctrl)
{
	int32_t rc = 0;
	uint32_t val = 0;
	int32_t read_words = 0, exp_words = 0;
	int32_t index = 0, first_byte = 0;
	uint32_t i = 0;
	enum cci_i2c_master_t master;
	enum cci_i2c_queue_t queue = QUEUE_1;
	struct cci_device *cci_dev = NULL;
	struct msm_camera_cci_i2c_read_cfg *read_cfg = NULL;
	CDBG("%s line %d\n", __func__, __LINE__);
	cci_dev = v4l2_get_subdevdata(sd);
	master = c_ctrl->cci_info->cci_i2c_master;
	read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
	mutex_lock(&cci_dev->cci_master_info[master].mutex);

	/*
	 * Call validate queue to make sure queue is empty before starting.
	 * If this call fails, don't proceed with i2c_read call. This is to
	 * avoid overflow / underflow of queue
	 */
	rc = msm_cci_validate_queue(cci_dev,
		cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1,
		master, queue);
	if (rc < 0) {
		pr_err("%s:%d Initial validataion failed rc %d\n", __func__,
			__LINE__, rc);
		goto ERROR;
	}

	if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
		pr_err("%s:%d More than max retries\n", __func__,
			__LINE__);
		goto ERROR;
	}

	if (read_cfg->data == NULL) {
		pr_err("%s:%d Data ptr is NULL\n", __func__,
			__LINE__);
		goto ERROR;
	}

	CDBG("%s master %d, queue %d\n", __func__, master, queue);
	CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
		c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
		c_ctrl->cci_info->id_map);
	val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
		c_ctrl->cci_info->retries << 16 |
		c_ctrl->cci_info->id_map << 18;
	rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
	if (rc < 0) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}

	val = CCI_I2C_LOCK_CMD;
	rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
	if (rc < 0) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}

	if (read_cfg->addr_type >= MSM_CAMERA_I2C_ADDR_TYPE_MAX) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}

	if (read_cfg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
		val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4) |
			((read_cfg->addr & 0xFF) << 8);
	if (read_cfg->addr_type == MSM_CAMERA_I2C_WORD_ADDR)
		val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4) |
			(((read_cfg->addr & 0xFF00) >> 8) << 8) |
			((read_cfg->addr & 0xFF) << 16);
	rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
	if (rc < 0) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}

	val = CCI_I2C_READ_CMD | (read_cfg->num_byte << 4);
	rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
	if (rc < 0) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}

	val = CCI_I2C_UNLOCK_CMD;
	rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
	if (rc < 0) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}

	val = msm_camera_io_r_mb(cci_dev->base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR
			+ master * 0x200 + queue * 0x100);
	CDBG("%s cur word cnt 0x%x\n", __func__, val);
	msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR
			+ master * 0x200 + queue * 0x100);

	val = 1 << ((master * 2) + queue);
	msm_camera_io_w_mb(val, cci_dev->base + CCI_QUEUE_START_ADDR);
	CDBG("%s:%d E wait_for_completion_timeout\n", __func__,
		__LINE__);
	rc = wait_for_completion_timeout(&cci_dev->
		cci_master_info[master].reset_complete, CCI_TIMEOUT);
	if (rc <= 0) {
		pr_err("%s: wait_for_completion_timeout %d\n",
			 __func__, __LINE__);
		if (rc == 0)
			rc = -ETIMEDOUT;
		msm_cci_flush_queue(cci_dev, master);
		goto ERROR;
	} else {
		rc = 0;
	}
	CDBG("%s:%d E wait_for_completion_timeout\n", __func__,
		__LINE__);

	read_words = msm_camera_io_r_mb(cci_dev->base +
		CCI_I2C_M0_READ_BUF_LEVEL_ADDR + master * 0x100);
	exp_words = ((read_cfg->num_byte / 4) + 1);
	if (read_words != exp_words) {
		pr_err("%s:%d read_words = %d, exp words = %d\n", __func__,
			__LINE__, read_words, exp_words);
		memset(read_cfg->data, 0, read_cfg->num_byte);
		rc = -EINVAL;
		goto ERROR;
	}
	index = 0;
	CDBG("%s index %d num_type %d\n", __func__, index,
		read_cfg->num_byte);
	first_byte = 0;
	do {
		val = msm_camera_io_r_mb(cci_dev->base +
			CCI_I2C_M0_READ_DATA_ADDR + master * 0x100);
		CDBG("%s read val 0x%x\n", __func__, val);
		for (i = 0; (i < 4) && (index < read_cfg->num_byte); i++) {
			CDBG("%s i %d index %d\n", __func__, i, index);
			if (!first_byte) {
				CDBG("%s sid 0x%x\n", __func__, val & 0xFF);
				first_byte++;
			} else {
				read_cfg->data[index] =
					(val  >> (i * 8)) & 0xFF;
				CDBG("%s data[%d] 0x%x\n", __func__, index,
					read_cfg->data[index]);
				index++;
			}
		}
	} while (--read_words > 0);
ERROR:
	mutex_unlock(&cci_dev->cci_master_info[master].mutex);
	return rc;
}

static int32_t msm_cci_i2c_read_bytes(struct v4l2_subdev *sd,
	struct msm_camera_cci_ctrl *c_ctrl)
{
	int32_t rc = 0;
	struct cci_device *cci_dev = NULL;
	enum cci_i2c_master_t master;
	struct msm_camera_cci_i2c_read_cfg *read_cfg = NULL;
	uint16_t read_bytes = 0;

	if (!sd || !c_ctrl) {
		pr_err("%s:%d sd %p c_ctrl %p\n", __func__,
			__LINE__, sd, c_ctrl);
		return -EINVAL;
	}
	if (!c_ctrl->cci_info) {
		pr_err("%s:%d cci_info NULL\n", __func__, __LINE__);
		return -EINVAL;
	}
	cci_dev = v4l2_get_subdevdata(sd);
	if (!cci_dev) {
		pr_err("%s:%d cci_dev NULL\n", __func__, __LINE__);
		return -EINVAL;
	}

	if (c_ctrl->cci_info->cci_i2c_master > MASTER_MAX
			|| c_ctrl->cci_info->cci_i2c_master < 0) {
		pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
		return -EINVAL;
	}

	master = c_ctrl->cci_info->cci_i2c_master;
	read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
	if ((!read_cfg->num_byte) || (read_cfg->num_byte > CCI_I2C_MAX_READ)) {
		pr_err("%s:%d read num bytes 0\n", __func__, __LINE__);
		rc = -EINVAL;
		goto ERROR;
	}

	read_bytes = read_cfg->num_byte;
	do {
		if (read_bytes > CCI_READ_MAX)
			read_cfg->num_byte = CCI_READ_MAX;
		else
			read_cfg->num_byte = read_bytes;
		rc = msm_cci_i2c_read(sd, c_ctrl);
		if (rc < 0) {
			pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
			goto ERROR;
		}
		if (read_bytes > CCI_READ_MAX) {
			read_cfg->addr += CCI_READ_MAX;
			read_cfg->data += CCI_READ_MAX;
			read_bytes -= CCI_READ_MAX;
		} else {
			read_bytes = 0;
		}
	} while (read_bytes);
ERROR:
	return rc;
}

static int32_t msm_cci_i2c_write(struct v4l2_subdev *sd,
	struct msm_camera_cci_ctrl *c_ctrl)
{
	int32_t rc = 0;
	struct cci_device *cci_dev;
	uint32_t val;
	enum cci_i2c_master_t master;
	enum cci_i2c_queue_t queue = QUEUE_0;
	cci_dev = v4l2_get_subdevdata(sd);
	if (c_ctrl->cci_info->cci_i2c_master > MASTER_MAX
			|| c_ctrl->cci_info->cci_i2c_master < 0) {
		pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
		return -EINVAL;
	}
	master = c_ctrl->cci_info->cci_i2c_master;
	CDBG("%s master %d, queue %d\n", __func__, master, queue);
	CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
		c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
		c_ctrl->cci_info->id_map);
	mutex_lock(&cci_dev->cci_master_info[master].mutex);

	/*
	 * Call validate queue to make sure queue is empty before starting.
	 * If this call fails, don't proceed with i2c_write call. This is to
	 * avoid overflow / underflow of queue
	 */
	rc = msm_cci_validate_queue(cci_dev,
		cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1,
		master, queue);
	if (rc < 0) {
		pr_err("%s:%d Initial validataion failed rc %d\n", __func__,
			__LINE__, rc);
		goto ERROR;
	}
	if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
		pr_err("%s:%d More than max retries\n", __func__,
			__LINE__);
		goto ERROR;
	}

	val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
		c_ctrl->cci_info->retries << 16 |
		c_ctrl->cci_info->id_map << 18;
	CDBG("%s:%d CCI_I2C_SET_PARAM_CMD\n", __func__, __LINE__);
	rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
	if (rc < 0) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}

	val = CCI_I2C_LOCK_CMD;
	CDBG("%s:%d CCI_I2C_LOCK_CMD\n", __func__, __LINE__);
	rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
	if (rc < 0) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}

	rc = msm_cci_data_queue(cci_dev, c_ctrl, queue);
	if (rc < 0) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}
	val = CCI_I2C_UNLOCK_CMD;
	CDBG("%s:%d CCI_I2C_UNLOCK_CMD\n", __func__, __LINE__);
	rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
	if (rc < 0) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}

	val = CCI_I2C_REPORT_CMD | (1 << 8);
	CDBG("%s:%d CCI_I2C_REPORT_CMD\n", __func__, __LINE__);
	rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
	if (rc < 0) {
		CDBG("%s failed line %d\n", __func__, __LINE__);
		goto ERROR;
	}

	val = msm_camera_io_r_mb(cci_dev->base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR
			+ master * 0x200 + queue * 0x100);
	CDBG("%s:%d cur word count %d\n", __func__, __LINE__, val);
	CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR\n", __func__, __LINE__);
	msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR
			+ master * 0x200 + queue * 0x100);

	val = 1 << ((master * 2) + queue);
	CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__);
	msm_camera_io_w_mb(val, cci_dev->base + CCI_QUEUE_START_ADDR);

	CDBG("%s:%d E wait_for_completion_timeout\n",
		__func__, __LINE__);
	rc = wait_for_completion_timeout(&cci_dev->
		cci_master_info[master].reset_complete, CCI_TIMEOUT);
	if (rc <= 0) {
		pr_err("%s: wait_for_completion_timeout %d\n",
			 __func__, __LINE__);
		if (rc == 0)
			rc = -ETIMEDOUT;
		msm_cci_flush_queue(cci_dev, master);
		goto ERROR;
	} else {
		rc = cci_dev->cci_master_info[master].status;
	}
	CDBG("%s:%d X wait_for_completion_timeout\n", __func__,
		__LINE__);

ERROR:
	mutex_unlock(&cci_dev->cci_master_info[master].mutex);
	return rc;
}

static int msm_cci_subdev_g_chip_ident(struct v4l2_subdev *sd,
			struct v4l2_dbg_chip_ident *chip)
{
	if (!chip) {
		pr_err("%s:%d: NULL pointer supplied for chip ident\n",
			 __func__, __LINE__);
		return -EINVAL;
	}
	chip->ident = V4L2_IDENT_CCI;
	chip->revision = 0;
	return 0;
}

static int32_t msm_cci_pinctrl_init(struct cci_device *cci_dev)
{
	struct msm_pinctrl_info *cci_pctrl = NULL;

	cci_pctrl = &cci_dev->cci_pinctrl;
	cci_pctrl->pinctrl = devm_pinctrl_get(&cci_dev->pdev->dev);
	if (IS_ERR_OR_NULL(cci_pctrl->pinctrl)) {
		pr_err("%s:%d devm_pinctrl_get cci_pinctrl failed\n",
			__func__, __LINE__);
		return -EINVAL;
	}
	cci_pctrl->gpio_state_active = pinctrl_lookup_state(
						cci_pctrl->pinctrl,
						CCI_PINCTRL_STATE_DEFAULT);
	if (IS_ERR_OR_NULL(cci_pctrl->gpio_state_active)) {
		pr_err("%s:%d look up state  for active state failed\n",
			__func__, __LINE__);
		return -EINVAL;
	}
	cci_pctrl->gpio_state_suspend = pinctrl_lookup_state(
						cci_pctrl->pinctrl,
						CCI_PINCTRL_STATE_SLEEP);
	if (IS_ERR_OR_NULL(cci_pctrl->gpio_state_suspend)) {
		pr_err("%s:%d look up state for suspend state failed\n",
			__func__, __LINE__);
		return -EINVAL;
	}
	return 0;
}

static int32_t msm_cci_init(struct v4l2_subdev *sd,
	struct msm_camera_cci_ctrl *c_ctrl)
{
	uint8_t i = 0;
	int32_t rc = 0, ret = 0;
	struct cci_device *cci_dev;
	enum cci_i2c_master_t master;

	cci_dev = v4l2_get_subdevdata(sd);
	if (!cci_dev || !c_ctrl) {
		pr_err("%s:%d failed: invalid params %p %p\n", __func__,
			__LINE__, cci_dev, c_ctrl);
		rc = -ENOMEM;
		return rc;
	}
	if (cci_dev->ref_count++) {
		CDBG("%s ref_count %d\n", __func__, cci_dev->ref_count);
		master = c_ctrl->cci_info->cci_i2c_master;
		CDBG("%s:%d master %d\n", __func__, __LINE__, master);
		msm_cci_set_clk_param(cci_dev, c_ctrl);
		if (master < MASTER_MAX && master >= 0) {
			mutex_lock(&cci_dev->cci_master_info[master].mutex);
			/* Set reset pending flag to TRUE */
			cci_dev->cci_master_info[master].reset_pending = TRUE;
			/* Set proper mask to RESET CMD address */
			if (master == MASTER_0)
				msm_camera_io_w_mb(CCI_M0_RESET_RMSK,
					cci_dev->base + CCI_RESET_CMD_ADDR);
			else
				msm_camera_io_w_mb(CCI_M1_RESET_RMSK,
					cci_dev->base + CCI_RESET_CMD_ADDR);
			/* wait for reset done irq */
			rc = wait_for_completion_timeout(
				&cci_dev->cci_master_info[master].
				reset_complete,
				CCI_TIMEOUT);
			if (rc <= 0)
				pr_err("%s:%d wait failed %d\n", __func__,
					__LINE__, rc);
			mutex_unlock(&cci_dev->cci_master_info[master].mutex);
		}
		return 0;
	}
	ret = msm_cci_pinctrl_init(cci_dev);
	if (ret < 0) {
		pr_err("%s:%d Initialization of pinctrl failed\n",
				__func__, __LINE__);
		cci_dev->cci_pinctrl_status = 0;
	} else {
		cci_dev->cci_pinctrl_status = 1;
	}
	rc = msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
		cci_dev->cci_gpio_tbl_size, 1);
	if (cci_dev->cci_pinctrl_status) {
		ret = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
				cci_dev->cci_pinctrl.gpio_state_active);
		if (ret)
			pr_err("%s:%d cannot set pin to active state\n",
				__func__, __LINE__);
	}
	if (rc < 0) {
		CDBG("%s: request gpio failed\n", __func__);
		goto request_gpio_failed;
	}
	cci_dev->reg_ptr = regulator_get(&(cci_dev->pdev->dev),
					 "qcom,gdscr-vdd");
	if (IS_ERR_OR_NULL(cci_dev->reg_ptr)) {
		pr_err(" %s: Failed in getting TOP gdscr regulator handle",
			__func__);
	} else {
		rc = regulator_enable(cci_dev->reg_ptr);
		if (rc) {
			pr_err(" %s: regulator enable failed for TOP GDSCR\n",
				__func__);
			goto clk_enable_failed;
		}
	}
	rc = msm_cam_clk_enable(&cci_dev->pdev->dev, cci_clk_info,
		cci_dev->cci_clk, cci_dev->num_clk, 1);
	if (rc < 0) {
		CDBG("%s: clk enable failed\n", __func__);
		goto clk_enable_failed;
	}
	enable_irq(cci_dev->irq->start);
	cci_dev->hw_version = msm_camera_io_r_mb(cci_dev->base +
		CCI_HW_VERSION_ADDR);
	pr_info("%s:%d: hw_version = 0x%x\n", __func__, __LINE__,
		cci_dev->hw_version);
	cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
	msm_camera_io_w_mb(CCI_RESET_CMD_RMSK, cci_dev->base +
			CCI_RESET_CMD_ADDR);
	msm_camera_io_w_mb(0x1, cci_dev->base + CCI_RESET_CMD_ADDR);
	rc = wait_for_completion_timeout(
		&cci_dev->cci_master_info[MASTER_0].reset_complete,
		CCI_TIMEOUT);
	if (rc <= 0) {
		pr_err("%s: wait_for_completion_timeout %d\n",
			 __func__, __LINE__);
		if (rc == 0)
			rc = -ETIMEDOUT;
		goto reset_complete_failed;
	}
	for (i = 0; i < MASTER_MAX; i++)
		cci_dev->master_clk_init[i] = 0;
	msm_cci_set_clk_param(cci_dev, c_ctrl);
	msm_camera_io_w_mb(CCI_IRQ_MASK_0_RMSK,
		cci_dev->base + CCI_IRQ_MASK_0_ADDR);
	msm_camera_io_w_mb(CCI_IRQ_MASK_0_RMSK,
		cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
	msm_camera_io_w_mb(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
	cci_dev->cci_state = CCI_STATE_ENABLED;

	return 0;

reset_complete_failed:
	disable_irq(cci_dev->irq->start);
	msm_cam_clk_enable(&cci_dev->pdev->dev, cci_clk_info,
		cci_dev->cci_clk, cci_dev->num_clk, 0);
clk_enable_failed:
	if (cci_dev->cci_pinctrl_status) {
		ret = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
				cci_dev->cci_pinctrl.gpio_state_suspend);
		if (ret)
			pr_err("%s:%d cannot set pin to suspend state\n",
				__func__, __LINE__);
	}
	msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
		cci_dev->cci_gpio_tbl_size, 0);

	if (!IS_ERR_OR_NULL(cci_dev->reg_ptr)) {
		regulator_disable(cci_dev->reg_ptr);
		regulator_put(cci_dev->reg_ptr);
		cci_dev->reg_ptr = NULL;
	}
request_gpio_failed:
	cci_dev->ref_count--;
	return rc;
}

static int32_t msm_cci_release(struct v4l2_subdev *sd)
{
	uint8_t i = 0, rc = 0;
	struct cci_device *cci_dev;

	cci_dev = v4l2_get_subdevdata(sd);
	if (!cci_dev->ref_count || cci_dev->cci_state != CCI_STATE_ENABLED) {
		pr_err("%s invalid ref count %d / cci state %d\n",
			__func__, cci_dev->ref_count, cci_dev->cci_state);
		return -EINVAL;
	}
	if (--cci_dev->ref_count) {
		CDBG("%s ref_count Exit %d\n", __func__, cci_dev->ref_count);
		return 0;
	}
	disable_irq(cci_dev->irq->start);
	msm_cam_clk_enable(&cci_dev->pdev->dev, cci_clk_info,
		cci_dev->cci_clk, cci_dev->num_clk, 0);
	if (!IS_ERR_OR_NULL(cci_dev->reg_ptr)) {
		regulator_disable(cci_dev->reg_ptr);
		regulator_put(cci_dev->reg_ptr);
		cci_dev->reg_ptr = NULL;
	}
	if (cci_dev->cci_pinctrl_status) {
		rc = pinctrl_select_state(cci_dev->cci_pinctrl.pinctrl,
				cci_dev->cci_pinctrl.gpio_state_suspend);
		if (rc)
			pr_err("%s:%d cannot set pin to active state\n",
				__func__, __LINE__);
	}
	cci_dev->cci_pinctrl_status = 0;
	msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
		cci_dev->cci_gpio_tbl_size, 0);
	for (i = 0; i < MASTER_MAX; i++)
		cci_dev->master_clk_init[i] = 0;
	cci_dev->cci_state = CCI_STATE_DISABLED;

	return 0;
}

static int32_t msm_cci_config(struct v4l2_subdev *sd,
	struct msm_camera_cci_ctrl *cci_ctrl)
{
	int32_t rc = 0;
	CDBG("%s line %d cmd %d\n", __func__, __LINE__,
		cci_ctrl->cmd);
	switch (cci_ctrl->cmd) {
	case MSM_CCI_INIT:
		rc = msm_cci_init(sd, cci_ctrl);
		break;
	case MSM_CCI_RELEASE:
		rc = msm_cci_release(sd);
		break;
	case MSM_CCI_I2C_READ:
		rc = msm_cci_i2c_read_bytes(sd, cci_ctrl);
		break;
	case MSM_CCI_I2C_WRITE:
		rc = msm_cci_i2c_write(sd, cci_ctrl);
		break;
	case MSM_CCI_GPIO_WRITE:
		break;
	default:
		rc = -ENOIOCTLCMD;
	}
	CDBG("%s line %d rc %d\n", __func__, __LINE__, rc);
	cci_ctrl->status = rc;
	return rc;
}

static irqreturn_t msm_cci_irq(int irq_num, void *data)
{
	uint32_t irq;
	struct cci_device *cci_dev = data;
	irq = msm_camera_io_r_mb(cci_dev->base + CCI_IRQ_STATUS_0_ADDR);
	msm_camera_io_w_mb(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
	msm_camera_io_w_mb(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
	msm_camera_io_w_mb(0x0, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
	CDBG("%s CCI_I2C_M0_STATUS_ADDR = 0x%x\n", __func__, irq);
	if (irq & CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK) {
		if (cci_dev->cci_master_info[MASTER_0].reset_pending == TRUE) {
			cci_dev->cci_master_info[MASTER_0].reset_pending =
				FALSE;
			complete(&cci_dev->cci_master_info[MASTER_0].
				reset_complete);
		}
		if (cci_dev->cci_master_info[MASTER_1].reset_pending == TRUE) {
			cci_dev->cci_master_info[MASTER_1].reset_pending =
				FALSE;
			complete(&cci_dev->cci_master_info[MASTER_1].
				reset_complete);
		}
	}
	if ((irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) ||
		(irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) ||
		(irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK)) {
		cci_dev->cci_master_info[MASTER_0].status = 0;
		complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
	}
	if ((irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) ||
		(irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) ||
		(irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK)) {
		cci_dev->cci_master_info[MASTER_1].status = 0;
		complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
	}
	if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
		cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
		msm_camera_io_w_mb(CCI_M0_RESET_RMSK,
			cci_dev->base + CCI_RESET_CMD_ADDR);
	}
	if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) {
		cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE;
		msm_camera_io_w_mb(CCI_M1_RESET_RMSK,
			cci_dev->base + CCI_RESET_CMD_ADDR);
	}
	if (irq & CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK) {
		pr_err("%s:%d MASTER_0 error 0x%x\n", __func__, __LINE__, irq);
		cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
		msm_camera_io_w_mb(CCI_M0_HALT_REQ_RMSK,
			cci_dev->base + CCI_HALT_REQ_ADDR);
	}
	if (irq & CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK) {
		pr_err("%s:%d MASTER_1 error 0x%x\n", __func__, __LINE__, irq);
		cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
		msm_camera_io_w_mb(CCI_M1_HALT_REQ_RMSK,
			cci_dev->base + CCI_HALT_REQ_ADDR);
	}
	return IRQ_HANDLED;
}

static int msm_cci_irq_routine(struct v4l2_subdev *sd, u32 status,
	bool *handled)
{
	struct cci_device *cci_dev = v4l2_get_subdevdata(sd);
	irqreturn_t ret;
	CDBG("%s line %d\n", __func__, __LINE__);
	ret = msm_cci_irq(cci_dev->irq->start, cci_dev);
	CDBG("%s: msm_cci_irq return %d\n", __func__, ret);
	*handled = TRUE;
	return 0;
}

static long msm_cci_subdev_ioctl(struct v4l2_subdev *sd,
	unsigned int cmd, void *arg)
{
	int32_t rc = 0;
	CDBG("%s line %d\n", __func__, __LINE__);
	switch (cmd) {
	case VIDIOC_MSM_CCI_CFG:
		rc = msm_cci_config(sd, arg);
		break;
	case MSM_SD_SHUTDOWN: {
		struct msm_camera_cci_ctrl ctrl_cmd;
		ctrl_cmd.cmd = MSM_CCI_RELEASE;
		rc = msm_cci_config(sd, &ctrl_cmd);
		break;
	}
	default:
		rc = -ENOIOCTLCMD;
	}
	CDBG("%s line %d rc %d\n", __func__, __LINE__, rc);
	return rc;
}

static struct v4l2_subdev_core_ops msm_cci_subdev_core_ops = {
	.g_chip_ident = &msm_cci_subdev_g_chip_ident,
	.ioctl = &msm_cci_subdev_ioctl,
	.interrupt_service_routine = msm_cci_irq_routine,
};

static const struct v4l2_subdev_ops msm_cci_subdev_ops = {
	.core = &msm_cci_subdev_core_ops,
};

static const struct v4l2_subdev_internal_ops msm_cci_internal_ops;

static void msm_cci_init_cci_params(struct cci_device *new_cci_dev)
{
	uint8_t i = 0, j = 0;
	for (i = 0; i < NUM_MASTERS; i++) {
		new_cci_dev->cci_master_info[i].status = 0;
		mutex_init(&new_cci_dev->cci_master_info[i].mutex);
		init_completion(&new_cci_dev->
			cci_master_info[i].reset_complete);
		for (j = 0; j < NUM_QUEUES; j++) {
			if (j == QUEUE_0)
				new_cci_dev->cci_i2c_queue_info[i][j].
					max_queue_size = CCI_I2C_QUEUE_0_SIZE;
			else
				new_cci_dev->cci_i2c_queue_info[i][j].
					max_queue_size = CCI_I2C_QUEUE_1_SIZE;
			}
	}
	return;
}

static int32_t msm_cci_init_gpio_params(struct cci_device *cci_dev)
{
	int32_t rc = 0, i = 0;
	uint32_t *val_array = NULL;
	uint8_t tbl_size = 0;
	struct device_node *of_node = cci_dev->pdev->dev.of_node;
	struct gpio *gpio_tbl = NULL;

	cci_dev->cci_gpio_tbl_size = tbl_size = of_gpio_count(of_node);
	CDBG("%s gpio count %d\n", __func__, tbl_size);
	if (!tbl_size) {
		pr_err("%s:%d gpio count 0\n", __func__, __LINE__);
		return 0;
	}

	gpio_tbl = cci_dev->cci_gpio_tbl =
		kzalloc(sizeof(struct gpio) * tbl_size, GFP_KERNEL);
	if (!gpio_tbl) {
		pr_err("%s failed %d\n", __func__, __LINE__);
		return 0;
	}

	for (i = 0; i < tbl_size; i++) {
		gpio_tbl[i].gpio = of_get_gpio(of_node, i);
		CDBG("%s gpio_tbl[%d].gpio = %d\n", __func__, i,
			gpio_tbl[i].gpio);
	}

	val_array = kzalloc(sizeof(uint32_t) * tbl_size, GFP_KERNEL);
	if (!val_array) {
		pr_err("%s failed %d\n", __func__, __LINE__);
		rc = -ENOMEM;
		goto ERROR1;
	}

	rc = of_property_read_u32_array(of_node, "qcom,gpio-tbl-flags",
		val_array, tbl_size);
	if (rc < 0) {
		pr_err("%s failed %d\n", __func__, __LINE__);
		goto ERROR2;
	}
	for (i = 0; i < tbl_size; i++) {
		gpio_tbl[i].flags = val_array[i];
		CDBG("%s gpio_tbl[%d].flags = %ld\n", __func__, i,
			gpio_tbl[i].flags);
	}

	for (i = 0; i < tbl_size; i++) {
		rc = of_property_read_string_index(of_node,
			"qcom,gpio-tbl-label", i, &gpio_tbl[i].label);
		CDBG("%s gpio_tbl[%d].label = %s\n", __func__, i,
			gpio_tbl[i].label);
		if (rc < 0) {
			pr_err("%s failed %d\n", __func__, __LINE__);
			goto ERROR2;
		}
	}

	kfree(val_array);
	return rc;

ERROR2:
	kfree(val_array);
ERROR1:
	kfree(cci_dev->cci_gpio_tbl);
	cci_dev->cci_gpio_tbl = NULL;
	cci_dev->cci_gpio_tbl_size = 0;
	return rc;
}

static void msm_cci_init_default_clk_params(struct cci_device *cci_dev,
	uint8_t index)
{
	/* default clock params are for 100Khz */
	cci_dev->cci_clk_params[index].hw_thigh = 78;
	cci_dev->cci_clk_params[index].hw_tlow = 114;
	cci_dev->cci_clk_params[index].hw_tsu_sto = 28;
	cci_dev->cci_clk_params[index].hw_tsu_sta = 28;
	cci_dev->cci_clk_params[index].hw_thd_dat = 10;
	cci_dev->cci_clk_params[index].hw_thd_sta = 77;
	cci_dev->cci_clk_params[index].hw_tbuf = 118;
	cci_dev->cci_clk_params[index].hw_scl_stretch_en = 0;
	cci_dev->cci_clk_params[index].hw_trdhld = 6;
	cci_dev->cci_clk_params[index].hw_tsp = 1;
}

static void msm_cci_init_clk_params(struct cci_device *cci_dev)
{
	int32_t rc = 0;
	uint32_t val = 0;
	uint8_t count = 0;
	struct device_node *of_node = cci_dev->pdev->dev.of_node;
	struct device_node *src_node = NULL;

	for (count = 0; count < I2C_MAX_MODES; count++) {

		if (I2C_STANDARD_MODE == count)
			src_node = of_find_node_by_name(of_node,
				"qcom,i2c_standard_mode");
		else if (I2C_FAST_MODE == count)
			src_node = of_find_node_by_name(of_node,
				"qcom,i2c_fast_mode");
		else
			src_node = of_find_node_by_name(of_node,
				"qcom,i2c_custom_mode");

		rc = of_property_read_u32(src_node, "qcom,hw-thigh", &val);
		CDBG("%s qcom,hw-thigh %d, rc %d\n", __func__, val, rc);
		if (!rc) {
			cci_dev->cci_clk_params[count].hw_thigh = val;
			rc = of_property_read_u32(src_node, "qcom,hw-tlow",
				&val);
			CDBG("%s qcom,hw-tlow %d, rc %d\n", __func__, val, rc);
		}
		if (!rc) {
			cci_dev->cci_clk_params[count].hw_tlow = val;
			rc = of_property_read_u32(src_node, "qcom,hw-tsu-sto",
				&val);
			CDBG("%s qcom,hw-tsu-sto %d, rc %d\n",
				__func__, val, rc);
		}
		if (!rc) {
			cci_dev->cci_clk_params[count].hw_tsu_sto = val;
			rc = of_property_read_u32(src_node, "qcom,hw-tsu-sta",
				&val);
			CDBG("%s qcom,hw-tsu-sta %d, rc %d\n",
				__func__, val, rc);
		}
		if (!rc) {
			cci_dev->cci_clk_params[count].hw_tsu_sta = val;
			rc = of_property_read_u32(src_node, "qcom,hw-thd-dat",
				&val);
			CDBG("%s qcom,hw-thd-dat %d, rc %d\n",
				__func__, val, rc);
		}
		if (!rc) {
			cci_dev->cci_clk_params[count].hw_thd_dat = val;
			rc = of_property_read_u32(src_node, "qcom,hw-thd-sta",
				&val);
			CDBG("%s qcom,hw-thd-sta %d, rc %d\n", __func__,
				val, rc);
		}
		if (!rc) {
			cci_dev->cci_clk_params[count].hw_thd_sta = val;
			rc = of_property_read_u32(src_node, "qcom,hw-tbuf",
				&val);
			CDBG("%s qcom,hw-tbuf %d, rc %d\n", __func__, val, rc);
		}
		if (!rc) {
			cci_dev->cci_clk_params[count].hw_tbuf = val;
			rc = of_property_read_u32(src_node,
				"qcom,hw-scl-stretch-en", &val);
			CDBG("%s qcom,hw-scl-stretch-en %d, rc %d\n",
				__func__, val, rc);
		}
		if (!rc) {
			cci_dev->cci_clk_params[count].hw_scl_stretch_en = val;
			rc = of_property_read_u32(src_node, "qcom,hw-trdhld",
				&val);
			CDBG("%s qcom,hw-trdhld %d, rc %d\n",
				__func__, val, rc);
		}
		if (!rc) {
			cci_dev->cci_clk_params[count].hw_trdhld = val;
			rc = of_property_read_u32(src_node, "qcom,hw-tsp",
				&val);
			CDBG("%s qcom,hw-tsp %d, rc %d\n", __func__, val, rc);
		}
		if (!rc)
			cci_dev->cci_clk_params[count].hw_tsp = val;
		else
			msm_cci_init_default_clk_params(cci_dev, count);
		of_node_put(src_node);
		src_node = NULL;
	}
	return;
}

struct v4l2_subdev *msm_cci_get_subdev(void)
{
	return g_cci_subdev;
}

static int msm_cci_get_clk_info(struct cci_device *cci_dev,
	struct platform_device *pdev)
{
	uint32_t count;
	int i, rc;
	uint32_t rates[CCI_NUM_CLK_MAX];

	struct device_node *of_node;
	of_node = pdev->dev.of_node;

	count = of_property_count_strings(of_node, "clock-names");
	cci_dev->num_clk = count;

	CDBG("%s: count = %d\n", __func__, count);
	if (count == 0) {
		pr_err("%s: no clocks found in device tree, count=%d",
			__func__, count);
		return 0;
	}

	if (count > CCI_NUM_CLK_MAX) {
		pr_err("%s: invalid count=%d, max is %d\n", __func__,
			count, CCI_NUM_CLK_MAX);
		return -EINVAL;
	}

	for (i = 0; i < count; i++) {
		rc = of_property_read_string_index(of_node, "clock-names",
				i, &(cci_clk_info[i].clk_name));
		CDBG("%s: clock-names[%d] = %s\n", __func__,
			i, cci_clk_info[i].clk_name);
		if (rc < 0) {
			pr_err("%s:%d, failed\n", __func__, __LINE__);
			return rc;
		}
	}
	rc = of_property_read_u32_array(of_node, "qcom,clock-rates",
		rates, count);
	if (rc < 0) {
		pr_err("%s:%d, failed", __func__, __LINE__);
		return rc;
	}
	for (i = 0; i < count; i++) {
		cci_clk_info[i].clk_rate = (rates[i] == 0) ?
			(long)-1 : rates[i];
		CDBG("%s: clk_rate[%d] = %ld\n", __func__, i,
			cci_clk_info[i].clk_rate);
	}
	return 0;
}

static uint32_t msm_get_cycles_per_ms(void)
{
	int i = 0;
	for (i = 0; i < CCI_NUM_CLK_MAX; i++) {
		if (!strcmp(cci_clk_info[i].clk_name, "cci_src_clk")) {
			CDBG("%s:%d i %d cci_src_clk\n",
				__func__, __LINE__, i);
			return ((cci_clk_info[i].clk_rate/1000)*256)/1000;
		}
	}
	pr_err("%s:%d, failed: Can use default: %d",
		__func__, __LINE__, CYCLES_PER_MICRO_SEC_DEFAULT);
	return CYCLES_PER_MICRO_SEC_DEFAULT;
}

static int msm_cci_probe(struct platform_device *pdev)
{
	struct cci_device *new_cci_dev;
	int rc = 0;
	CDBG("%s: pdev %p device id = %d\n", __func__, pdev, pdev->id);
	new_cci_dev = kzalloc(sizeof(struct cci_device), GFP_KERNEL);
	if (!new_cci_dev) {
		CDBG("%s: no enough memory\n", __func__);
		return -ENOMEM;
	}
	v4l2_subdev_init(&new_cci_dev->msm_sd.sd, &msm_cci_subdev_ops);
	new_cci_dev->msm_sd.sd.internal_ops = &msm_cci_internal_ops;
	snprintf(new_cci_dev->msm_sd.sd.name,
			ARRAY_SIZE(new_cci_dev->msm_sd.sd.name), "msm_cci");
	v4l2_set_subdevdata(&new_cci_dev->msm_sd.sd, new_cci_dev);
	platform_set_drvdata(pdev, &new_cci_dev->msm_sd.sd);
	CDBG("%s sd %p\n", __func__, &new_cci_dev->msm_sd.sd);
	if (pdev->dev.of_node)
		of_property_read_u32((&pdev->dev)->of_node,
			"cell-index", &pdev->id);

	new_cci_dev->reg_ptr = NULL;
	rc = msm_cci_get_clk_info(new_cci_dev, pdev);
	if (rc < 0) {
		pr_err("%s: msm_cci_get_clk_info() failed", __func__);
		return -EFAULT;
	}

	new_cci_dev->cycles_per_us = msm_get_cycles_per_ms();
	new_cci_dev->ref_count = 0;
	new_cci_dev->mem = platform_get_resource_byname(pdev,
					IORESOURCE_MEM, "cci");
	if (!new_cci_dev->mem) {
		CDBG("%s: no mem resource?\n", __func__);
		rc = -ENODEV;
		goto cci_no_resource;
	}
	new_cci_dev->irq = platform_get_resource_byname(pdev,
					IORESOURCE_IRQ, "cci");
	CDBG("%s line %d cci irq start %d end %d\n", __func__,
		__LINE__,
		(int) new_cci_dev->irq->start,
		(int) new_cci_dev->irq->end);
	if (!new_cci_dev->irq) {
		CDBG("%s: no irq resource?\n", __func__);
		rc = -ENODEV;
		goto cci_no_resource;
	}
	new_cci_dev->io = request_mem_region(new_cci_dev->mem->start,
		resource_size(new_cci_dev->mem), pdev->name);
	if (!new_cci_dev->io) {
		CDBG("%s: no valid mem region\n", __func__);
		rc = -EBUSY;
		goto cci_no_resource;
	}

	new_cci_dev->base = ioremap(new_cci_dev->mem->start,
		resource_size(new_cci_dev->mem));
	if (!new_cci_dev->base) {
		rc = -ENOMEM;
		goto cci_release_mem;
	}
	rc = request_irq(new_cci_dev->irq->start, msm_cci_irq,
		IRQF_TRIGGER_RISING, "cci", new_cci_dev);
	if (rc < 0) {
		CDBG("%s: irq request fail\n", __func__);
		rc = -EBUSY;
		goto cci_release_mem;
	}
	disable_irq(new_cci_dev->irq->start);
	new_cci_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x6;
	msm_sd_register(&new_cci_dev->msm_sd);
	new_cci_dev->pdev = pdev;
	msm_cci_init_cci_params(new_cci_dev);
	msm_cci_init_clk_params(new_cci_dev);
	msm_cci_init_gpio_params(new_cci_dev);
	rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
	if (rc)
		pr_err("%s: failed to add child nodes, rc=%d\n", __func__, rc);
	new_cci_dev->cci_state = CCI_STATE_DISABLED;
	g_cci_subdev = &new_cci_dev->msm_sd.sd;
	CDBG("%s cci subdev %p\n", __func__, &new_cci_dev->msm_sd.sd);
	CDBG("%s line %d\n", __func__, __LINE__);
	return 0;

cci_release_mem:
	release_mem_region(new_cci_dev->mem->start,
		resource_size(new_cci_dev->mem));
cci_no_resource:
	kfree(new_cci_dev);
	return 0;
}

static int msm_cci_exit(struct platform_device *pdev)
{
	struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
	struct cci_device *cci_dev =
		v4l2_get_subdevdata(subdev);
	release_mem_region(cci_dev->mem->start, resource_size(cci_dev->mem));
	kfree(cci_dev);
	return 0;
}

static const struct of_device_id msm_cci_dt_match[] = {
	{.compatible = "qcom,cci"},
	{}
};

MODULE_DEVICE_TABLE(of, msm_cci_dt_match);

static struct platform_driver cci_driver = {
	.probe = msm_cci_probe,
	.remove = msm_cci_exit,
	.driver = {
		.name = MSM_CCI_DRV_NAME,
		.owner = THIS_MODULE,
		.of_match_table = msm_cci_dt_match,
	},
};

static int __init msm_cci_init_module(void)
{
	return platform_driver_register(&cci_driver);
}

static void __exit msm_cci_exit_module(void)
{
	platform_driver_unregister(&cci_driver);
}

module_init(msm_cci_init_module);
module_exit(msm_cci_exit_module);
MODULE_DESCRIPTION("MSM CCI driver");
MODULE_LICENSE("GPL v2");
